package com.lyf.scm.plan.domain.service.impl;

import com.github.pagehelper.PageInfo;
import com.lyf.scm.common.constants.CommonConstants;
import com.lyf.scm.common.enums.*;
import com.lyf.scm.common.model.BaseSearchDTO;
import com.lyf.scm.common.model.ExcelExportDataDTO;
import com.lyf.scm.common.model.SalesPlanSelectDTO;
import com.lyf.scm.common.util.AdvancedSearchUtil;
import com.lyf.scm.common.util.ExcelExportUtil;
import com.lyf.scm.common.util.date.MyDateUtil;
import com.lyf.scm.common.util.style.DirectlyAccuracyCellWriterHandler;
import com.lyf.scm.plan.api.dto.*;
import com.lyf.scm.plan.domain.convertor.SalesPlanDirectlyAccuracyConvertor;
import com.lyf.scm.plan.domain.remote.basedata.dto.StoreDTO;
import com.lyf.scm.plan.domain.remote.basedata.facade.BaseDataFacade;
import com.lyf.scm.plan.domain.remote.item.dto.SkuUnitDTO;
import com.lyf.scm.plan.domain.remote.item.facade.ItemCoreFacade;
import com.lyf.scm.plan.domain.repository.*;
import com.lyf.scm.plan.domain.service.SalesPlanDirectlyAccuracyService;
import com.lyf.scm.plan.insfrastructure.db.dataobject.SalesPlanSelectDO;
import com.lyf.scm.plan.insfrastructure.db.dataobject.bigdata.*;
import com.rome.arch.core.exception.RomeException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.lyf.scm.common.util.BatchHandlerUtil.splitTo;
import static com.lyf.scm.plan.domain.util.BigDataCacheUtil.*;
import static java.util.stream.Collectors.groupingBy;

/**
 * 直营预测计划_销售计划准确率
 *
 * @author wangchlei
 * @date 2020/08/13
 */
@Slf4j
@Service
public class SalesPlanDirectlyAccuracyServiceImpl implements SalesPlanDirectlyAccuracyService {
    @Resource
    private SalesPlanDirectlyAccuracyRepository salesPlanDirectlyAccuracyRepository;
    @Resource
    private SalesPlanDirectlyAccuracyConvertor salesPlanDirectlyAccuracyConvertor;
    @Resource
    private ItemCoreFacade itemCoreFacade;
    @Resource
    private SalesPlanDirectlyRepository salesPlanDirectlyRepository;
    @Resource
    private SalesPlanDirectlyInfoRepository salesPlanDirectlyInfoRepository;
    @Resource
    private BaseDataFacade baseDataFacade;
    @Resource
    private RoleAuthorityAreaRepository roleAuthorityAreaRepository;
    @Resource
    private OperationLogRecordRepository operationLogRecordRepository;

    /**
     * 按条件查询直营预测计划_销售计划准确率(周)
     *
     * @param salesPlanDirectlyAccuracyDTO
     * @return {@link PageInfo < SalePlanDirectlyDifferinInfoDTO >}
     **/
    @Override
    public PageInfo<SalesPlanDirectlyAccuracyInfoDTO> listWeekBySelective(SalesPlanDirectlyAccuracyDTO salesPlanDirectlyAccuracyDTO) {
        //校验角色区域权限是否正确（同时赋值区域搜索条件）
        this.checkRoleArea(salesPlanDirectlyAccuracyDTO);
        //获取mapper查询条件
        SalesPlanSelectDO salesPlanSelectDO = salesPlanDirectlyAccuracyConvertor.dtoToDO(salesPlanDirectlyAccuracyDTO);
        //非分页查询
        List<SalesPlanDirectlyAccuracyInfoDTO> salesPlanDirectlyAccuracyInfoDTOs = salesPlanDirectlyAccuracyRepository.listByCycles(salesPlanSelectDO);
        if (CollectionUtils.isEmpty(salesPlanDirectlyAccuracyInfoDTOs)) {
            return new PageInfo<>();
        }
        //获取所有查询条件标识
        List<String> conditions = AdvancedSearchUtil.getConditions(salesPlanDirectlyAccuracyDTO);
        //获取所有去重后的skuCode
        List<String> skuCodes = salesPlanDirectlyAccuracyInfoDTOs.stream().map(SalesPlanDirectlyAccuracyInfoDTO::getSkuCode).distinct().collect(Collectors.toList());
        Map<String, BigDecimal> map = this.getBaseSkuUnitRate(skuCodes, salesPlanDirectlyAccuracyDTO.getUnitType());
        //统计不同维度时间周期的长度
        int elementsSize = AdvancedSearchUtil.getElementsSize(salesPlanDirectlyAccuracyDTO.getWeekDateList());
        //获取分页数据
        int startIndex = (salesPlanDirectlyAccuracyDTO.getPageNum() - 1) * salesPlanDirectlyAccuracyDTO.getPageSize();
        int endIndex = salesPlanDirectlyAccuracyDTO.getPageNum() * salesPlanDirectlyAccuracyDTO.getPageSize() - 1;
        //组下标
        AtomicInteger groupIndex = new AtomicInteger(0);
        //最终结果
        List<SalesPlanDirectlyAccuracyInfoDTO> pageList = new ArrayList<>();
        Map<String, SalesPlanDirectlyAccuracyInfoDTO> polymerizationResult = salesPlanDirectlyAccuracyInfoDTOs.stream().collect(groupingBy(item -> AdvancedSearchUtil.fetchGroupKey(item, conditions), TreeMap::new, Collectors.collectingAndThen(Collectors.toList(), values -> {
            if (groupIndex.get() < startIndex || groupIndex.get() > endIndex) {
                groupIndex.incrementAndGet();
                return null;
            }
            List<BigDecimal> predictedQtys = new ArrayList<>();
            List<BigDecimal> actualSalesQtys = new ArrayList<>();
            values.forEach(item -> {
                if (map.get(item.getSkuCode()) == null) {
                    throw new RomeException("999", "无法获取单位比例转换信息：" + "skuCode," + item.getSkuCode() + "; 单位类型" + SkuUnitTypeEnum.getNameByType(salesPlanDirectlyAccuracyDTO.getUnitType()));
                }
                predictedQtys.addAll(Arrays.stream(item.getPredictedQtyStr().split(":")).map(subItem -> new BigDecimal(subItem).divide(map.get(item.getSkuCode()), 5, BigDecimal.ROUND_HALF_UP)).collect(Collectors.toList()));
                actualSalesQtys.addAll(Arrays.stream(item.getActualSalesQtyStr().split(":")).map(subItem -> new BigDecimal(subItem).divide(map.get(item.getSkuCode()), 5, BigDecimal.ROUND_HALF_UP)).collect(Collectors.toList()));
            });
            //总长度
            int totalSize = predictedQtys.size();
            List<BigDecimal> predictedQty = AdvancedSearchUtil.getMergeResult(predictedQtys, elementsSize, totalSize);
            List<BigDecimal> actualSalesQty = AdvancedSearchUtil.getMergeResult(actualSalesQtys, elementsSize, totalSize);
            List<BigDecimal> forecastAccuracy = AdvancedSearchUtil.getForecastAccuracys(predictedQty, actualSalesQty, elementsSize);
            List<BigDecimal> planAccuracy = AdvancedSearchUtil.getForecastAccuracys(predictedQty, actualSalesQty, elementsSize);
            values.get(0).setPredictedQty(predictedQty);
            values.get(0).setPlanQty(predictedQty);
            values.get(0).setActualSalesQty(actualSalesQty);
            values.get(0).setForecastAccuracy(forecastAccuracy);
            values.get(0).setPlanAccuracy(planAccuracy);
            //赋值label标签
            this.setLable(values.get(0));
            //赋值查询条件
            AdvancedSearchUtil.setGroupAdvancedSearchCondition(values.get(0), conditions);
            pageList.add(values.get(0));
            groupIndex.incrementAndGet();
            return values.get(0);
        })));
        //设置实际确认量
        this.setActualConfirmQty(salesPlanDirectlyAccuracyDTO, conditions, pageList, elementsSize);
        //获取分页数据
        PageInfo<SalesPlanDirectlyAccuracyInfoDTO> pageInfo = new PageInfo<>();
        pageInfo.setTotal(polymerizationResult.values().size());
        pageInfo.setList(pageList);
        return pageInfo;
    }

    /**
     * 设置实际确认量
     *
     * @param salesPlanDirectlyAccuracyDTO
     * @param conditions
     * @param pageList
     * @param elementsSize
     */
    private void setActualConfirmQty(SalesPlanDirectlyAccuracyDTO salesPlanDirectlyAccuracyDTO, List<String> conditions, List<SalesPlanDirectlyAccuracyInfoDTO> pageList, int elementsSize) {
        SalesPlanSelectDO salesPlanSelectDO = this.getQueryCondition(salesPlanDirectlyAccuracyDTO, pageList, conditions);
        List<SalesPlanDirectlyConditionDTO> salesPlanDirectlyDTOS = salesPlanDirectlyRepository.listByCondition(salesPlanSelectDO);
        Map<String, List<SalesPlanDirectlyInfoDTO>> conditionDtos = new HashMap<>();
        Map<String, BigDecimal> map = new HashMap<>();
        if (!CollectionUtils.isEmpty(salesPlanDirectlyDTOS)) {
            List<SalesPlanDirectlyInfoDTO> salesPlanDirectlyInfoDTOs = new ArrayList<>();
            List<List<Long>> lists = splitTo(salesPlanDirectlyDTOS.stream().map(SalesPlanDirectlyConditionDTO::getId).collect(Collectors.toList()), 5000);
            for (List<Long> list : lists) {
                List<SalesPlanDirectlyInfoDTO> itemList = salesPlanDirectlyInfoRepository.listByDirectlyId(list, salesPlanDirectlyAccuracyDTO.getCreator());
                salesPlanDirectlyInfoDTOs.addAll(itemList);
            }
            if (!CollectionUtils.isEmpty(salesPlanDirectlyInfoDTOs)) {
                Map<Long, SalesPlanDirectlyInfoDTO> salesPlanDirectlyInfoDTOsMap = salesPlanDirectlyInfoDTOs.stream().collect(Collectors.toMap(SalesPlanDirectlyInfoDTO::getDirectlyId, item -> item));
                //根据搜索条件分组 条件——主表id集合
                conditionDtos = salesPlanDirectlyDTOS.stream().collect(groupingBy(item -> AdvancedSearchUtil.fetchGroupKey(item, conditions), Collectors.collectingAndThen(Collectors.toList(), values -> {
                    List<Long> ids = values.stream().map(SalesPlanDirectlyConditionDTO::getId).collect(Collectors.toList());
                    //该集合可能为空
                    List<SalesPlanDirectlyInfoDTO> result = new ArrayList<>();
                    ids.forEach(element -> {
                        if (salesPlanDirectlyInfoDTOsMap.get(element) != null) {
                            result.add(salesPlanDirectlyInfoDTOsMap.get(element));
                        }
                    });
                    return result;
                })));
            }
            //获取所有skucode
            List<String> recivedSkuCodes = salesPlanDirectlyDTOS.stream().map(SalesPlanDirectlyConditionDTO::getSkuCode).distinct().collect(Collectors.toList());
            map = this.getBaseSkuUnitRate(recivedSkuCodes, salesPlanDirectlyAccuracyDTO.getUnitType());
        }
        Map<String, List<SalesPlanDirectlyInfoDTO>> finalConditionDtos = conditionDtos;
        Map<String, BigDecimal> finalMap = map;
        BigDecimal zero = new BigDecimal("0");
        pageList.forEach(item -> {
            //获取符合条件的主表id集合
            List<SalesPlanDirectlyInfoDTO> result = finalConditionDtos.get(AdvancedSearchUtil.fetchGroupKey(item, conditions));
            if (!CollectionUtils.isEmpty(result)) {
                Map<Date, BigDecimal> dateNum = result.stream().collect(groupingBy(SalesPlanDirectlyInfoDTO::getPlanStartDate, Collectors.collectingAndThen(Collectors.toList(), subValues -> subValues.stream().map(element -> element.getConfirmQty().divide(finalMap.get(element.getSkuCode()), 5, BigDecimal.ROUND_HALF_UP)).reduce(BigDecimal.ZERO, BigDecimal::add))));
                List<BigDecimal> finalActualConfirmQty = new ArrayList<>();
                salesPlanDirectlyAccuracyDTO.getWeekDateList().forEach(subItem -> {
                    Date startTime = subItem.get("startTime");
                    finalActualConfirmQty.add(dateNum.getOrDefault(startTime, zero));
                });
                item.setActualConfirmQty(finalActualConfirmQty);
            } else {
                item.setActualConfirmQty(AdvancedSearchUtil.getBlankCount(elementsSize));
            }
        });
    }

    /**
     * 设置实际确认量(图表)
     *
     * @param salesPlanDirectlyAccuracyDTO
     * @param conditions
     * @param valuesResult
     * @param elementsSize
     */
    private void setActualConfirmQtyChart(SalesPlanDirectlyAccuracyDTO salesPlanDirectlyAccuracyDTO, List<String> conditions, Map<String, List<BigDecimal>> valuesResult, int elementsSize) {
        SalesPlanSelectDO salesPlanSelectDO = this.getQueryCondition(salesPlanDirectlyAccuracyDTO, conditions);
        List<SalesPlanDirectlyConditionDTO> salesPlanDirectlyDTOS = salesPlanDirectlyRepository.listByCondition(salesPlanSelectDO);
        List<SalesPlanDirectlyInfoDTO> dtos = null;
        Map<String, BigDecimal> map = new HashMap<>();
        if (!CollectionUtils.isEmpty(salesPlanDirectlyDTOS)) {
            List<SalesPlanDirectlyInfoDTO> salesPlanDirectlyInfoDTOs = salesPlanDirectlyInfoRepository.listByDirectlyId(salesPlanDirectlyDTOS.stream().map(SalesPlanDirectlyConditionDTO::getId).collect(Collectors.toList()), salesPlanDirectlyAccuracyDTO.getCreator());
            if (!CollectionUtils.isEmpty(salesPlanDirectlyInfoDTOs)) {
                Map<Long, SalesPlanDirectlyInfoDTO> salesPlanDirectlyInfoDTOsMap = salesPlanDirectlyInfoDTOs.stream().collect(Collectors.toMap(SalesPlanDirectlyInfoDTO::getDirectlyId, item -> item));
                //根据搜索条件分组 条件——主表id集合
                dtos = salesPlanDirectlyDTOS.stream().collect(Collectors.collectingAndThen(Collectors.toList(), values -> {
                    List<Long> ids = values.stream().map(SalesPlanDirectlyConditionDTO::getId).collect(Collectors.toList());
                    //该集合可能为空
                    List<SalesPlanDirectlyInfoDTO> result = new ArrayList<>();
                    ids.forEach(element -> {
                        if (salesPlanDirectlyInfoDTOsMap.get(element) != null) {
                            result.add(salesPlanDirectlyInfoDTOsMap.get(element));
                        }
                    });
                    return result;
                }));
            }
            //获取所有skucode
            List<String> recivedSkuCodes = salesPlanDirectlyDTOS.stream().map(SalesPlanDirectlyConditionDTO::getSkuCode).distinct().collect(Collectors.toList());
            map = this.getBaseSkuUnitRate(recivedSkuCodes, salesPlanDirectlyAccuracyDTO.getUnitType());
        }
        Map<String, BigDecimal> finalMap = map;
        BigDecimal zero = new BigDecimal("0");
        if (!CollectionUtils.isEmpty(dtos)) {
            Map<Date, BigDecimal> dateNum = dtos.stream().collect(groupingBy(SalesPlanDirectlyInfoDTO::getPlanStartDate, Collectors.collectingAndThen(Collectors.toList(), subValues -> subValues.stream().map(element -> element.getConfirmQty().multiply(finalMap.get(element.getSkuCode()))).reduce(BigDecimal.ZERO, BigDecimal::add))));
            List<BigDecimal> actualConfirmQty = new ArrayList<>();
            salesPlanDirectlyAccuracyDTO.getWeekDateList().forEach(subItem -> {
                Date startTime = subItem.get("startTime");
                actualConfirmQty.add(dateNum.getOrDefault(startTime, zero));
            });
            valuesResult.put(SalesPlanDirectlyAccuracyEnum.ACTUAL_CONFIRM_QTY.getKey(), actualConfirmQty);
        } else {
            valuesResult.put(SalesPlanDirectlyAccuracyEnum.ACTUAL_CONFIRM_QTY.getKey(), AdvancedSearchUtil.getBlankCount(elementsSize));
        }
    }

    /**
     * 图表
     *
     * @param baseSearchDTO
     * @param conditions
     * @return
     */
    private SalesPlanSelectDO getQueryCondition(BaseSearchDTO baseSearchDTO, List<String> conditions) {
        SalesPlanSelectDO salesPlanSelectDO = new SalesPlanSelectDO();
        if (conditions.contains(BaseSearchColumnEnum.PROVINCE_CODE.getColumn())) {
            salesPlanSelectDO.setProvinceCodeList(baseSearchDTO.getProvinceCodeList());
        }
        if (conditions.contains(BaseSearchColumnEnum.CITY_CODE.getColumn())) {
            salesPlanSelectDO.setCityCodeList(baseSearchDTO.getCityCodeList());
        }
        if (conditions.contains(BaseSearchColumnEnum.COUNTY_CODE.getColumn())) {
            salesPlanSelectDO.setCountyCodeList(baseSearchDTO.getCountyCodeList());
        }
        if (conditions.contains(BaseSearchColumnEnum.STORE_CODE.getColumn())) {
            salesPlanSelectDO.setStoreCodeList(baseSearchDTO.getStoreCodeList());
        }
        salesPlanSelectDO.setStartTime(baseSearchDTO.getStartTime());
        salesPlanSelectDO.setEndTime(baseSearchDTO.getEndTime());
        return salesPlanSelectDO;
    }

    private SalesPlanSelectDO getQueryCondition(BaseSearchDTO baseSearchDTO, List<SalesPlanDirectlyAccuracyInfoDTO> pageList, List<String> conditions) {
        SalesPlanSelectDO salesPlanSelectDO = new SalesPlanSelectDO();
        if (conditions.contains(BaseSearchColumnEnum.PROVINCE_CODE.getColumn())) {
            salesPlanSelectDO.setProvinceCodeList(pageList.stream().map(SalesPlanDirectlyAccuracyInfoDTO::getProvinceCode).distinct().collect(Collectors.toList()));
        }
        if (conditions.contains(BaseSearchColumnEnum.CITY_CODE.getColumn())) {
            salesPlanSelectDO.setCityCodeList(pageList.stream().map(SalesPlanDirectlyAccuracyInfoDTO::getCityCode).distinct().collect(Collectors.toList()));
        }
        if (conditions.contains(BaseSearchColumnEnum.COUNTY_CODE.getColumn())) {
            salesPlanSelectDO.setCountyCodeList(pageList.stream().map(SalesPlanDirectlyAccuracyInfoDTO::getCountyCode).distinct().collect(Collectors.toList()));
        }
        if (conditions.contains(BaseSearchColumnEnum.STORE_CODE.getColumn())) {
            salesPlanSelectDO.setStoreCodeList(pageList.stream().map(SalesPlanDirectlyAccuracyInfoDTO::getStoreCode).distinct().collect(Collectors.toList()));
        }
        if (conditions.contains(BaseSearchColumnEnum.PRI_CATEGORY_CODE.getColumn())) {
            salesPlanSelectDO.setPriCategoryCodeList(pageList.stream().map(SalesPlanDirectlyAccuracyInfoDTO::getPriCategoryCode).distinct().collect(Collectors.toList()));
        }
        if (conditions.contains(BaseSearchColumnEnum.SEC_CATEGORY_CODE.getColumn())) {
            salesPlanSelectDO.setSecCategoryCodeList(pageList.stream().map(SalesPlanDirectlyAccuracyInfoDTO::getSecCategoryCode).distinct().collect(Collectors.toList()));
        }
        if (conditions.contains(BaseSearchColumnEnum.TER_CATEGORY_CODE.getColumn())) {
            salesPlanSelectDO.setTerCategoryCodeList(pageList.stream().map(SalesPlanDirectlyAccuracyInfoDTO::getTerCategoryCode).distinct().collect(Collectors.toList()));
        }
        if (conditions.contains(BaseSearchColumnEnum.SKU_CODE.getColumn())) {
            salesPlanSelectDO.setSkuCodeList(pageList.stream().map(SalesPlanDirectlyAccuracyInfoDTO::getSkuCode).distinct().collect(Collectors.toList()));
        }
        salesPlanSelectDO.setStartTime(baseSearchDTO.getStartTime());
        salesPlanSelectDO.setEndTime(baseSearchDTO.getEndTime());
        return salesPlanSelectDO;
    }

    /**
     * 获取sku基础单位运算为其它单位的比例
     *
     * @param skuCodes
     * @param unitType
     * @return {@link Map<BigDecimal>}
     **/
    private Map<String, BigDecimal> getBaseSkuUnitRate(List<String> skuCodes, Long unitType) {
        Map<String, BigDecimal> result;
        BigDecimal bigDecimal = new BigDecimal("1");
        if (SkuUnitTypeEnum.BASIS_UNIT.getUnitType().equals(unitType)) {
            result = skuCodes.stream().collect(Collectors.toMap(item -> item, item -> bigDecimal));
        } else {
            List<SkuUnitDTO> purchaseUnitRatio = itemCoreFacade.getConversionRatio(skuCodes, unitType);
            result = purchaseUnitRatio.stream().collect(Collectors.toMap(SkuUnitDTO::getSkuCode, item -> BigDecimal.valueOf(item.getScale())));
        }
        return result;
    }

    /**
     * 校验角色区域信息
     *
     * @param baseSearchDTO
     * @return void
     **/
    private void checkRoleArea(BaseSearchDTO baseSearchDTO) {
        //从角色区域权限表获取对应区域
        List<RoleAuthorityAreaDTO> roleAuthorityAreaDTOS = roleAuthorityAreaRepository.listByUserId(baseSearchDTO.getCreator());
        if (CollectionUtils.isEmpty(roleAuthorityAreaDTOS)) {
            throw new RomeException("999", "该角色的无任何区域权限");
        }
        //获取该角色的区域信息
        List<SalesPlanSelectDTO> salesPlanSelectDTOS = roleAuthorityAreaDTOS.stream().filter(item -> item.getChannelCode() == null || "".equals(item.getChannelCode())).map(item -> {
            SalesPlanSelectDTO salesPlanSelectDTO = new SalesPlanSelectDTO();
            if (item.getCountyCode() != null && !"".equals(item.getCountyCode())) {
                salesPlanSelectDTO.setLevel(CommonConstants.AREA_LEVEL_THREE);
                salesPlanSelectDTO.setCode(item.getCountyCode());
                return salesPlanSelectDTO;
            }
            if (item.getCityCode() != null && !"".equals(item.getCityCode())) {
                salesPlanSelectDTO.setLevel(CommonConstants.AREA_LEVEL_TWO);
                salesPlanSelectDTO.setCode(item.getCityCode());
                return salesPlanSelectDTO;
            }
            if (item.getProvinceCode() != null && !"".equals(item.getProvinceCode())) {
                salesPlanSelectDTO.setLevel(CommonConstants.AREA_LEVEL_ONE);
                salesPlanSelectDTO.setCode(item.getProvinceCode());
                return salesPlanSelectDTO;
            }
            throw new RomeException("999", "数据库角色区域权限有错误");
        }).collect(Collectors.toList());
        //将其统一到最低层级
        List<SalesPlanSelectDTO> roleAreas = baseDataFacade.getSubAreaCode(salesPlanSelectDTOS);
        //如果有查询条件
        if (!CollectionUtils.isEmpty(baseSearchDTO.getAreaCodeList())) {
            //将查询条件汇总到最低层级
            List<SalesPlanSelectDTO> searchAreas = baseDataFacade.getSubAreaCode(baseSearchDTO.getAreaCodeList());
            List<SalesPlanSelectDTO> result = null;
            if (searchAreas.get(0).getLevel() > roleAreas.get(0).getLevel()) {
                result = this.recursionLevel(roleAreas, searchAreas.get(0).getLevel());
                if (!result.containsAll(searchAreas)) {
                    throw new RomeException(ResponseMsg.FAIL_1906.getCode(), ResponseMsg.FAIL_1906.getMsg());
                }
            }
            if (searchAreas.get(0).getLevel().equals(roleAreas.get(0).getLevel())) {
                if (!roleAreas.containsAll(searchAreas)) {
                    throw new RomeException(ResponseMsg.FAIL_1906.getCode(), ResponseMsg.FAIL_1906.getMsg());
                }
            }
            if (searchAreas.get(0).getLevel() < roleAreas.get(0).getLevel()) {
                result = this.recursionLevel(searchAreas, roleAreas.get(0).getLevel());
                if (!roleAreas.containsAll(result)) {
                    throw new RomeException(ResponseMsg.FAIL_1906.getCode(), ResponseMsg.FAIL_1906.getMsg());
                }
            }
            setAreaSearch(searchAreas, baseSearchDTO);
        } else {
            setAreaSearch(roleAreas, baseSearchDTO);
        }
    }

    private void setAreaSearch(List<SalesPlanSelectDTO> areas, BaseSearchDTO baseSearchDTO) {
        if (CommonConstants.AREA_LEVEL_ONE.equals(areas.get(0).getLevel())) {
            baseSearchDTO.setProvinceCodeList(areas.stream().map(SalesPlanSelectDTO::getCode).distinct().collect(Collectors.toList()));
        }
        if (CommonConstants.AREA_LEVEL_TWO.equals(areas.get(0).getLevel())) {
            baseSearchDTO.setCityCodeList(areas.stream().map(SalesPlanSelectDTO::getCode).distinct().collect(Collectors.toList()));
        }
        if (CommonConstants.AREA_LEVEL_THREE.equals(areas.get(0).getLevel())) {
            baseSearchDTO.setCountyCodeList(areas.stream().map(SalesPlanSelectDTO::getCode).distinct().collect(Collectors.toList()));
        }
        if (CommonConstants.AREA_LEVEL_FOUR.equals(areas.get(0).getLevel())) {
            baseSearchDTO.setStoreCodeList(areas.stream().map(SalesPlanSelectDTO::getCode).distinct().collect(Collectors.toList()));
        }
    }

    private List<SalesPlanSelectDTO> recursionLevel(List<SalesPlanSelectDTO> searchAreas, Integer level) {
        List<String> codeList = searchAreas.stream().map(SalesPlanSelectDTO::getCode).collect(Collectors.toList());
        if (level.equals(CommonConstants.AREA_LEVEL_FOUR)) {
            List<StoreDTO> storeList = new ArrayList<>();
            if (searchAreas.get(0).getLevel().equals(CommonConstants.AREA_LEVEL_ONE)) {
                storeList = baseDataFacade.ListStoreByArea(codeList, CommonConstants.AREA_LEVEL_ONE, 1, 3000);
            }
            if (searchAreas.get(0).getLevel().equals(CommonConstants.AREA_LEVEL_TWO)) {
                storeList = baseDataFacade.ListStoreByArea(codeList, CommonConstants.AREA_LEVEL_TWO, 1, 3000);
            }
            if (searchAreas.get(0).getLevel().equals(CommonConstants.AREA_LEVEL_THREE)) {
                storeList = baseDataFacade.ListStoreByArea(codeList, CommonConstants.AREA_LEVEL_THREE, 1, 3000);
            }
            List<SalesPlanSelectDTO> areaList = storeList.stream().map(item -> {
                SalesPlanSelectDTO salesPlanSelectDTO = new SalesPlanSelectDTO();
                salesPlanSelectDTO.setCode(item.getCode());
                salesPlanSelectDTO.setLevel(level);
                return salesPlanSelectDTO;
            }).collect(Collectors.toList());
            return areaList;
        } else {
            if (searchAreas.get(0).getLevel() < level) {
                List<SalesPlanSelectDTO> areas = baseDataFacade.batchSearchByParentCode(codeList).stream().map(item -> {
                    SalesPlanSelectDTO salesPlanSelectDTO = new SalesPlanSelectDTO();
                    salesPlanSelectDTO.setCode(item.getCode());
                    salesPlanSelectDTO.setLevel(item.getLevel());
                    return salesPlanSelectDTO;
                }).collect(Collectors.toList());
                return this.recursionLevel(areas, level);
            }
        }
        return searchAreas;
    }

    /**
     * 设置标签
     *
     * @param salesPlanDirectlyAccuracyInfoDTO
     * @return void
     **/
    private void setLable(SalesPlanDirectlyAccuracyInfoDTO salesPlanDirectlyAccuracyInfoDTO) {
        salesPlanDirectlyAccuracyInfoDTO.setPlanQtyLabel(SalesPlanDirectlyAccuracyEnum.PLAN_QTY.getDesc());
        salesPlanDirectlyAccuracyInfoDTO.setForecastAccuracyLabel(SalesPlanDirectlyAccuracyEnum.FORECAST_ACCURACY.getDesc());
        salesPlanDirectlyAccuracyInfoDTO.setPlanAccuracyLabel(SalesPlanDirectlyAccuracyEnum.PLAN_ACCURACY.getDesc());
        salesPlanDirectlyAccuracyInfoDTO.setPredictedQtyLabel(SalesPlanDirectlyAccuracyEnum.PREDICTED_QTY.getDesc());
        salesPlanDirectlyAccuracyInfoDTO.setActualConfirmQtyLabel(SalesPlanDirectlyAccuracyEnum.ACTUAL_CONFIRM_QTY.getDesc());
        salesPlanDirectlyAccuracyInfoDTO.setActualSalesQtyLabel(SalesPlanDirectlyAccuracyEnum.ACTUAL_SALES_QTY.getDesc());
    }

    /**
     * 图表数据接口
     *
     * @param salesPlanDirectlyAccuracyDTO
     * @return {@link EChartsDTO}
     **/
    @Override
    public EChartsDTO eChartsData(SalesPlanDirectlyAccuracyDTO salesPlanDirectlyAccuracyDTO) {
        //校验角色区域权限是否正确（同时赋值区域搜索条件）
        this.checkRoleArea(salesPlanDirectlyAccuracyDTO);
        SalesPlanSelectDO salesPlanSelectDO = salesPlanDirectlyAccuracyConvertor.dtoToDO(salesPlanDirectlyAccuracyDTO);
        List<SalesPlanDirectlyAccuracyInfoDTO> salesPlanDirectlyAccuracyInfoDTOs = salesPlanDirectlyAccuracyRepository.eChartsData(salesPlanSelectDO);
        if (CollectionUtils.isEmpty(salesPlanDirectlyAccuracyInfoDTOs)) {
            return new EChartsDTO();
        }
        //获取所有去重后的skuCode
        List<String> skuCodes = salesPlanDirectlyAccuracyInfoDTOs.stream().map(SalesPlanDirectlyAccuracyInfoDTO::getSkuCode).distinct().collect(Collectors.toList());
        Map<String, BigDecimal> map = this.getBaseSkuUnitRate(skuCodes, salesPlanDirectlyAccuracyDTO.getUnitType());
        //统计不同维度时间周期的长度
        int elementsSize = AdvancedSearchUtil.getElementsSize(salesPlanDirectlyAccuracyDTO.getWeekDateList());
        //将整个list中元素聚合
        Map<String, List<BigDecimal>> valuesResult = salesPlanDirectlyAccuracyInfoDTOs.stream().collect(Collectors.collectingAndThen(Collectors.toList(), values -> {
            //遍历每一组的集合，将对应参数聚合，然后赋值给第一个元素，将第一个元素返回
            List<BigDecimal> predictedQtys = new ArrayList<>();
            List<BigDecimal> actualSalesQtys = new ArrayList<>();
            values.forEach(item -> {
                if (map.get(item.getSkuCode()) == null) {
                    throw new RomeException("999", "无法获取单位比例转换信息：" + "skuCode," + item.getSkuCode() + "; 单位类型" + SkuUnitTypeEnum.getNameByType(salesPlanDirectlyAccuracyDTO.getUnitType()));
                }
                predictedQtys.addAll(Arrays.stream(item.getPredictedQtyStr().split(":")).map(subItem -> new BigDecimal(subItem).divide(map.get(item.getSkuCode()), 5, BigDecimal.ROUND_HALF_UP)).collect(Collectors.toList()));
                actualSalesQtys.addAll(Arrays.stream(item.getActualSalesQtyStr().split(":")).map(subItem -> new BigDecimal(subItem).divide(map.get(item.getSkuCode()), 5, BigDecimal.ROUND_HALF_UP)).collect(Collectors.toList()));
            });
            //总长度
            int totalSize = predictedQtys.size();
            List<BigDecimal> predictedQty = AdvancedSearchUtil.getMergeResult(predictedQtys, elementsSize, totalSize);
            List<BigDecimal> actualSalesQty = AdvancedSearchUtil.getMergeResult(actualSalesQtys, elementsSize, totalSize);
            List<BigDecimal> forecastAccuracy = AdvancedSearchUtil.getForecastAccuracys(predictedQty, actualSalesQty, elementsSize);
            List<BigDecimal> planAccuracy = AdvancedSearchUtil.getForecastAccuracys(predictedQty, actualSalesQty, elementsSize);
            Map<String, List<BigDecimal>> result = new HashMap<>();
            result.put(SalesPlanDirectlyAccuracyEnum.PREDICTED_QTY.getKey(), predictedQty);
            result.put(SalesPlanDirectlyAccuracyEnum.PLAN_QTY.getKey(), predictedQty);
            result.put(SalesPlanDirectlyAccuracyEnum.ACTUAL_SALES_QTY.getKey(), actualSalesQty);
            result.put(SalesPlanDirectlyAccuracyEnum.FORECAST_ACCURACY.getKey(), forecastAccuracy);
            result.put(SalesPlanDirectlyAccuracyEnum.PLAN_ACCURACY.getKey(), planAccuracy);
            return result;
        }));
        //设置实际确认量
        List<String> conditions = AdvancedSearchUtil.getConditions(salesPlanDirectlyAccuracyDTO);
        this.setActualConfirmQtyChart(salesPlanDirectlyAccuracyDTO, conditions, valuesResult, elementsSize);
        EChartsDTO eChartsDTO = new EChartsDTO();
        eChartsDTO.setDateList(MyDateUtil.processCyclesDate(salesPlanDirectlyAccuracyDTO.getWeekDateList(), "M.dd"));
        eChartsDTO.setLabelData(this.getAllKeyLabels(SalesPlanDirectlyAccuracyEnum.values()));
        eChartsDTO.setValue(valuesResult);
        return eChartsDTO;
    }

    private List<EChartsDTOLabelDataDTO> getAllKeyLabels(SalesPlanDirectlyAccuracyEnum[] values) {
        List<EChartsDTOLabelDataDTO> list = new ArrayList<>();
        for (SalesPlanDirectlyAccuracyEnum value : values) {
            EChartsDTOLabelDataDTO eChartsDTOLabelDataDTO = new EChartsDTOLabelDataDTO();
            eChartsDTOLabelDataDTO.setKey(value.getKey());
            eChartsDTOLabelDataDTO.setLabel(value.getDesc());
            list.add(eChartsDTOLabelDataDTO);
        }
        return list;
    }

    /**
     * 导出所有查询数据
     *
     * @param salesPlanDirectlyAccuracyDTO
     * @param type
     * @return {@link ExcelExportDataDTO}
     **/
    @Override
    public ExcelExportDataDTO export(SalesPlanDirectlyAccuracyDTO salesPlanDirectlyAccuracyDTO, Integer type) {
        //校验角色区域权限是否正确（同时赋值区域搜索条件）
        this.checkRoleArea(salesPlanDirectlyAccuracyDTO);
        //获取mapper查询条件
        SalesPlanSelectDO salesPlanSelectDO = salesPlanDirectlyAccuracyConvertor.dtoToDO(salesPlanDirectlyAccuracyDTO);
        //非分页查询
        List<SalesPlanDirectlyAccuracyInfoDTO> salesPlanDirectlyAccuracyInfoDTOs = salesPlanDirectlyAccuracyRepository.searchedDataExport(salesPlanSelectDO);
        if (CollectionUtils.isEmpty(salesPlanDirectlyAccuracyInfoDTOs)) {
            throw new RomeException(ResponseMsg.FAIL_2005.getCode(), ResponseMsg.FAIL_2005.getMsg());
        }
        //获取所有去重后的skuCode
        List<String> skuCodes = salesPlanDirectlyAccuracyInfoDTOs.stream().map(SalesPlanDirectlyAccuracyInfoDTO::getSkuCode).distinct().collect(Collectors.toList());
        Map<String, BigDecimal> map = this.getBaseSkuUnitRate(skuCodes, salesPlanDirectlyAccuracyDTO.getUnitType());
        //获取所有查询条件标识
        List<String> conditions = AdvancedSearchUtil.getConditions(salesPlanDirectlyAccuracyDTO);
        //获取日期标题
        List<String> dateTitles = MyDateUtil.processCyclesDate(salesPlanDirectlyAccuracyDTO.getWeekDateList(), "MM.dd");
        //获取excel标题 同时返回高级搜索标题的数量
        List<List<String>> heads = new ArrayList<>();
        int mergeCellsColumns = AdvancedSearchUtil.setRedirectExcelTitles(heads, conditions, dateTitles);
        //统计不同维度时间周期的长度
        int elementsSize = AdvancedSearchUtil.getElementsSize(salesPlanDirectlyAccuracyDTO.getWeekDateList());
        //获取指标名称集合
        List<String> targets = SalesPlanDirectlyAccuracyEnum.getAllTargetsDesc();
        //获取excel数据
        List<List<Object>> exportDataList = new ArrayList<>();
        //处理聚合结果
        Map<String, SalesPlanDirectlyAccuracyInfoDTO> polymerizationResult = null;
        List<SalesPlanDirectlyAccuracyInfoDTO> pageList = new ArrayList<>();
        //全部数据导出
        if (ExcelExportTypeEnum.SEARCHED_DATA_EXPORT.getType().equals(type)) {
            polymerizationResult = salesPlanDirectlyAccuracyInfoDTOs.stream().collect(groupingBy(item -> AdvancedSearchUtil.fetchGroupKey(item, conditions), TreeMap::new, Collectors.collectingAndThen(Collectors.toList(), values -> {
                this.handleExcelDataGroup(values, elementsSize, map, conditions, pageList, salesPlanDirectlyAccuracyDTO.getUnitType());
                return null;
            })));
        }
        //当前页数据导出
        if (ExcelExportTypeEnum.CURRENT_PAGE_EXPORT.getType().equals(type)) {
            //获取分页数据
            int startIndex = (salesPlanDirectlyAccuracyDTO.getPageNum() - 1) * salesPlanDirectlyAccuracyDTO.getPageSize();
            int endIndex = salesPlanDirectlyAccuracyDTO.getPageNum() * salesPlanDirectlyAccuracyDTO.getPageSize() - 1;
            //组下标
            AtomicInteger groupIndex = new AtomicInteger(0);
            polymerizationResult = salesPlanDirectlyAccuracyInfoDTOs.stream().collect(groupingBy(item -> AdvancedSearchUtil.fetchGroupKey(item, conditions), TreeMap::new, Collectors.collectingAndThen(Collectors.toList(), values -> {
                if (groupIndex.get() < startIndex || groupIndex.get() > endIndex) {
                    groupIndex.incrementAndGet();
                    return null;
                }
                this.handleExcelDataGroup(values, elementsSize, map, conditions, pageList, salesPlanDirectlyAccuracyDTO.getUnitType());
                groupIndex.incrementAndGet();
                return null;
            })));
        }
        //当前页数据导出
        if (ExcelExportTypeEnum.CHECKED_BOX_EXPORT.getType().equals(type)) {
            //获取分页数据
            int startIndex = (salesPlanDirectlyAccuracyDTO.getPageNum() - 1) * salesPlanDirectlyAccuracyDTO.getPageSize();
            List<Integer> checkedIndex = salesPlanDirectlyAccuracyDTO.getRowIndex().stream().map(item -> item + startIndex).collect(Collectors.toList());
            //组下标
            AtomicInteger groupIndex = new AtomicInteger(0);
            polymerizationResult = salesPlanDirectlyAccuracyInfoDTOs.stream().collect(groupingBy(item -> AdvancedSearchUtil.fetchGroupKey(item, conditions), TreeMap::new, Collectors.collectingAndThen(Collectors.toList(), values -> {
                if (!checkedIndex.contains(groupIndex.get())) {
                    groupIndex.incrementAndGet();
                    return null;
                }
                this.handleExcelDataGroup(values, elementsSize, map, conditions, pageList, salesPlanDirectlyAccuracyDTO.getUnitType());
                groupIndex.incrementAndGet();
                return null;
            })));
        }
        //设置实际确认量
        this.setActualConfirmQty(salesPlanDirectlyAccuracyDTO, conditions, pageList, elementsSize);
        //设置导出数据
        this.setExportDataList(exportDataList, pageList, targets, dateTitles);
        log.info("SIZE{}", polymerizationResult.size());
        return ExcelExportUtil.getExcelExportData(mergeCellsColumns, exportDataList, heads, targets.size(), new DirectlyAccuracyCellWriterHandler());
    }

    private void setExportDataList(List<List<Object>> exportDataList, List<SalesPlanDirectlyAccuracyInfoDTO> pageList, List<String> targets, List<String> dateTitles) {
        BigDecimal zero = new BigDecimal("0");
        pageList.forEach(element -> {
            int actualIndex = 0;
            for (String target : targets) {
                List<Object> itemRow = new ArrayList<>();
                //加入，区域，品类，商品  信息
                AdvancedSearchUtil.setRedirectExcelRowAdvancedSearchCondition(element, itemRow);
                //加入指标
                itemRow.add(target);
                //加入数据
                for (int j = 0; j < dateTitles.size(); j++) {
//获取指标集合，这里没有排序，默认使用枚举的顺序
                    switch (actualIndex) {
                        case 0:
                            itemRow.add(element.getPredictedQty().get(j).compareTo(zero) == 0 ? "-" : element.getPredictedQty().get(j));
                            break;
                        case 1:
                            itemRow.add(element.getPredictedQty().get(j).compareTo(zero) == 0 ? "-" : element.getPredictedQty().get(j));
                            break;
                        case 2:
                            itemRow.add(element.getActualConfirmQty().get(j).compareTo(zero) == 0 ? "-" : element.getActualConfirmQty().get(j));
                            break;
                        case 3:
                            itemRow.add(element.getActualSalesQty().get(j).compareTo(zero) == 0 ? "-" : element.getActualSalesQty().get(j));
                            break;
                        case 4:
                            itemRow.add(element.getForecastAccuracy().get(j).compareTo(zero) == 0 ? "-" : element.getForecastAccuracy().get(j));
                            break;
                        case 5:
                            itemRow.add(element.getPlanAccuracy().get(j).compareTo(zero) == 0 ? "-" : element.getPlanAccuracy().get(j));
                            break;
                        default:
                            break;
                    }
                }
                actualIndex++;
                exportDataList.add(itemRow);
            }
        });
    }

    private void handleExcelDataGroup(List<SalesPlanDirectlyAccuracyInfoDTO> values, int elementsSize, Map<String, BigDecimal> map, List<String> conditions, List<SalesPlanDirectlyAccuracyInfoDTO> pageList, Long unitType) {
        //遍历每一组的集合，将对应参数聚合，然后赋值给第一个元素，将第一个元素返回
        List<BigDecimal> predictedQtys = new ArrayList<>();
        List<BigDecimal> actualSalesQtys = new ArrayList<>();
        values.forEach(item -> {
            if (map.get(item.getSkuCode()) == null) {
                throw new RomeException("999", "无法获取单位比例转换信息：" + "skuCode," + item.getSkuCode() + "; 单位类型" + SkuUnitTypeEnum.getNameByType(unitType));
            }
            predictedQtys.addAll(Arrays.stream(item.getPredictedQtyStr().split(":")).map(subItem -> new BigDecimal(subItem).divide(map.get(item.getSkuCode()), 5, BigDecimal.ROUND_HALF_UP)).collect(Collectors.toList()));
            actualSalesQtys.addAll(Arrays.stream(item.getActualSalesQtyStr().split(":")).map(subItem -> new BigDecimal(subItem).divide(map.get(item.getSkuCode()), 5, BigDecimal.ROUND_HALF_UP)).collect(Collectors.toList()));
        });
        //总长度
        int totalSize = predictedQtys.size();
        List<BigDecimal> predictedQty = AdvancedSearchUtil.getMergeResult(predictedQtys, elementsSize, totalSize);
        List<BigDecimal> actualSalesQty = AdvancedSearchUtil.getMergeResult(actualSalesQtys, elementsSize, totalSize);
        List<BigDecimal> forecastAccuracy = AdvancedSearchUtil.getForecastAccuracys(predictedQty, actualSalesQty, elementsSize);
        List<BigDecimal> planAccuracy = AdvancedSearchUtil.getForecastAccuracys(predictedQty, actualSalesQty, elementsSize);
        values.get(0).setPredictedQty(predictedQty);
        values.get(0).setPlanQty(predictedQty);
        values.get(0).setActualSalesQty(actualSalesQty);
        values.get(0).setForecastAccuracy(forecastAccuracy);
        values.get(0).setPlanAccuracy(planAccuracy);
        //赋值查询条件
        AdvancedSearchUtil.setGroupAdvancedSearchCondition(values.get(0), conditions);
        pageList.add(values.get(0));
    }

    /**
     * 删除当前日期之后的所有数据(处理大数据)
     *
     * @param
     * @param storeCode
     * @return void
     **/
    @Transactional(rollbackFor = Exception.class)
    public void handleBigData(List<DirectlyAccuracyInDO> directlyAccuracyInDOs, String storeCode) {
        salesPlanDirectlyAccuracyRepository.batchDeleteByStartDate(storeCode, weekStartDate);
        if (!CollectionUtils.isEmpty(directlyAccuracyInDOs)) {
            List<List<DirectlyAccuracyInDO>> lists = splitTo(directlyAccuracyInDOs, 5000);
            for (List<DirectlyAccuracyInDO> list : lists) {
                salesPlanDirectlyAccuracyRepository.batchInsert(list);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleRemedialBigData(List<DirectlyAccuracyInDO> directlyAccuracyInDOs, String storeCode) {
        //删除当前日期之后的所有数据
        salesPlanDirectlyAccuracyRepository.batchDeleteByStartDate(storeCode, weekStartDate);
        if (!CollectionUtils.isEmpty(directlyAccuracyInDOs)) {
            List<List<DirectlyAccuracyInDO>> lists = splitTo(directlyAccuracyInDOs, 5000);
            for (List<DirectlyAccuracyInDO> list : lists) {
                salesPlanDirectlyAccuracyRepository.batchInsert(list);
            }
        }
        //删除错误日志信息
        operationLogRecordRepository.deleteBigDataErrorLog(OperationDataEnum.DIRECTLY_ACCURACY.getTableType(), startTime, endTime, storeCode);
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleUpBigData(List<DirectlyAccuracyInDO> directlyAccuracyInDOs, List<DirectlyAccuracyUpDO> directlyAccuracyUpDOs, List<Long> ids) {
        //先删除
        if (!CollectionUtils.isEmpty(ids)) {
            salesPlanDirectlyAccuracyRepository.rmByIds(ids);
        }
        //更新
        if (!CollectionUtils.isEmpty(directlyAccuracyUpDOs)) {
            List<List<DirectlyAccuracyUpDO>> lists = splitTo(directlyAccuracyUpDOs, 2000);
            for (List<DirectlyAccuracyUpDO> list : lists) {
                salesPlanDirectlyAccuracyRepository.upByIds(list);
            }
        }
        if (!CollectionUtils.isEmpty(directlyAccuracyInDOs)) {
            List<List<DirectlyAccuracyInDO>> lists = splitTo(directlyAccuracyInDOs, 5000);
            for (List<DirectlyAccuracyInDO> list : lists) {
                salesPlanDirectlyAccuracyRepository.batchInsert(list);
            }
        }
    }

    @Override
    public void delete(String opSign) {
        salesPlanDirectlyAccuracyRepository.delete(opSign);
    }
}

